﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management.Automation;
using System.Reflection;
using DotNet.DevOps.Tasks.Util;
using Microsoft.Build.Framework;
using NUnit.Framework;

namespace DotNet.DevOps.Tasks.Tests.Extensions
{
    internal static class Extensions
    {
        internal static void AssertThatLatestEMLFileContainsThisString(this String @this, string content)
        {
            var filesOrderByDate = Directory.GetFiles(@this, "*.eml").OrderByDescending(file => new FileInfo(file).CreationTime);

            var fileContent = File.ReadAllText(filesOrderByDate.First());

            Assert.That(fileContent.Contains(content), Is.True);
        }

        internal static String GetPath(this String @this, string srcDirectory)
        {
            return Path.Combine(Environment.CurrentDirectory, srcDirectory, @this);
        }

        internal static String PrefixWithTestname(this String @this)
        {
            var newFilename = string.Format("{0}_{1}", new StackTrace().GetFrame(1).GetMethod().Name, Path.GetFileName(@this));
            return @this.Replace(Path.GetFileName(@this), newFilename);
        }

        internal static T SetAllStandardStringProperties<T>(this T @this)
        {
            var properties = GetNonRequiredStringProperties(@this)
                .Where(prop => Attribute.IsDefined(prop, typeof(StandardAttribute)) == true).ToList();

            properties.ForEach(prop => prop.SetValue(@this, prop.Name, null));

            return @this;
        }

        private static List<PropertyInfo> GetNonRequiredStringProperties<T>(this T @this)
        {
            return typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
                                            .Where(x => x.PropertyType == typeof(String) &&
                                                        Attribute.IsDefined(x, typeof(RequiredAttribute)) == false).ToList();
        }

        internal static List<String> GetExpectedNonRequiredStringPropertyEntries(this AssemblyInfo.AssemblyInfo @this)
        {
            var properties = GetNonRequiredStringProperties(@this)
                                .Where(prop => String.IsNullOrEmpty(prop.GetValue(@this, null) as String) == false).ToList();

            var entries = new List<String>();
            properties.ForEach(prop => entries.Add(string.Format("{0}(\"{1}\")", prop.Name, prop.GetValue(@this, null) as String)));

            return entries;
        }

        internal static Dictionary<String, String> GetPropertiesAsStringNameValuePairs<T>(this T @this)
        {
            return @this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
                                    .Where(prop => Attribute.IsDefined(prop, typeof(RequiredAttribute)) == true ||
                                                   Attribute.IsDefined(prop, typeof(SpecialAttribute)) == true ||
                                                   Attribute.IsDefined(prop, typeof(StandardAttribute)) == true)
                                    .Where(prop => prop.GetValue(@this, null) != null)
                                        .ToDictionary(prop => prop.Name, prop => prop.GetValue(@this, null).ToString());
        }

        internal static ITask SetAllPublicProperties(this ITask @this)
        {
            var properties = @this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
                                                .Where(prop => Attribute.IsDefined(prop, typeof (SpecialAttribute)) == true ||
                                                               Attribute.IsDefined(prop, typeof (StandardAttribute)) == true ||
                                                               Attribute.IsDefined(prop, typeof (RequiredAttribute)) == true)
                                                .Where(prop => prop.PropertyType == typeof (String)).ToList();

            properties.ForEach(prop => prop.SetValue(@this, prop.Name, null));

            return @this;
        }

        internal static Dictionary<String, String> GetAllOutputProperties(this ITask @this)
        {
            return @this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
                                                .Where(prop => Attribute.IsDefined(prop, typeof(OutputAttribute)) == true)
                                                .ToDictionary(prop => prop.Name, prop => (prop.GetValue(@this, null) ?? "").ToString());
        }

        internal static Cmdlet DuplicateAllIdenticalProperties(this Cmdlet @this, ITask task)
        {
            var cmdletProps = @this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            var taskProps = task.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);

            cmdletProps.Where(cprop => taskProps.Where(tprop => tprop.Name == cprop.Name).Count() > 0)
                .ToList()
                .ForEach(cprop => cprop.SetValue(@this, taskProps.Where(tprop => tprop.Name == cprop.Name).First().GetValue(task,null),null));

            return @this;
        }
    }
}
